package com.ajim.mvc.servlet;

import com.ajim.mvc.anno.MyAutoWired;
import com.ajim.mvc.anno.MyController;
import com.ajim.mvc.anno.MyRequestMapping;
import com.ajim.mvc.anno.MyService;
import com.ajim.mvc.pojo.Handler;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyDispatcherServlet extends HttpServlet {
    /*对象属性的使用要先进行初始化,否则下面直接调用会报空指针*/
    private Properties properties = new Properties();
    private List<String> classNames = new ArrayList<>();
    private Map<String, Object> ioc = new HashMap<>();
    //private Map<String, Method> handlerMapping = new HashMap<>();
    private List<Handler> handlerMapping = new ArrayList<>();

    @Override
    public void init(ServletConfig config) throws ServletException {
        //读取配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        //读取配置文件--获取包
        doLoadConfig(contextConfigLocation);
        //扫描包
        String scanPackage = properties.getProperty("scanPackage");
        doScanPackage(scanPackage);
        //包下面的注解--初始化bean
        doInitInstance();
        //初始化之后开始进行依赖注入
        doAutoWired();
        //以来注入之后初始化--HandlerMapping
        initHandlerMapping();
        System.out.println("DispatcherServlet初始化完成,开始接受请求....");
    }

    private void initHandlerMapping() {
        //建立url和method的映射
        if (ioc.isEmpty()) return;
        Collection<Object> values = ioc.values();
        Set<Object> objects = new HashSet<>();
        objects.addAll(values);
        String baseUrl = "";
        for (Object object : objects) {
            Class<?> aClass = object.getClass();
            if (aClass.isAnnotationPresent(MyService.class)) {
                continue;
            }
            if (aClass.isAnnotationPresent(MyRequestMapping.class)) {
                baseUrl = aClass.getAnnotation(MyRequestMapping.class).value().trim();
            }
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (!method.isAnnotationPresent(MyRequestMapping.class)) {
                    continue;
                }
                if (method.isAnnotationPresent(MyRequestMapping.class)) {
                    String methodUrl = method.getAnnotation(MyRequestMapping.class).value().trim();
                    String url = baseUrl + methodUrl;
                    Handler handler = new Handler(object, Pattern.compile(url), method);
                    Map<String, Integer> parameterIndex = handler.getParameterIndex();
                    Parameter[] parameters = method.getParameters();
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];
                        if (parameter.getType().getSimpleName().equals("HttpServletRequest") || parameter.getType().getSimpleName().equals("HttpServletResponse")) {
                            parameterIndex.put(parameter.getType().getSimpleName(), i);
                        } else {
                            parameterIndex.put(parameter.getName(), i);
                        }
                    }
                    handlerMapping.add(handler);
                }
            }
        }
    }

    //解决依赖注入
    private void doAutoWired() {
        if (ioc.isEmpty()) return;
        Collection<Object> values = ioc.values();
        Set<Object> objects = new HashSet<>();
        objects.addAll(values);
        for (Object object : objects) {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(MyAutoWired.class)) {
                    MyAutoWired annotation = field.getAnnotation(MyAutoWired.class);
                    String s = annotation.value();
                    Object o = null;
                    if (!"".equals(s.trim())) {
                        o = ioc.get(s);
                    }
                    if (o == null) {
                        String name = field.getType().getSimpleName();
                        name = lowerFirst(name);
                        o = ioc.get(name);
                    }
                    if (o == null) {
                        String name = field.getType().getName();
                        o = ioc.get(name);
                    }
                    field.setAccessible(true);
                    try {
                        field.set(object, o);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }


    }


    private void doInitInstance() {
        if (classNames.size() == 0) return;
        for (String className : classNames) {
            try {
                Class<?> aClass = Class.forName(className);
                if (aClass.isInterface()) {
                    /*我们扫描到的类里面有接口*/
                    continue;
                }
                Object o = aClass.getDeclaredConstructor().newInstance();
                if (aClass.isAnnotationPresent(MyController.class)) {
                    String simpleName = aClass.getSimpleName();
                    String lowerFirstSimpleName = lowerFirst(simpleName);
                    ioc.put(lowerFirstSimpleName, o);
                } else if (aClass.isAnnotationPresent(MyService.class)) {
                    MyService myService = aClass.getAnnotation(MyService.class);
                    String annoValue = myService.value();
                    if (!"".equals(annoValue.trim())) {
                        ioc.put(annoValue, o);
                    }
                    //将简单类名 -- 映射向这个对象
                    String simpleName = aClass.getSimpleName();
                    String lowerFirstSimpleName = lowerFirst(simpleName);
                    ioc.put(lowerFirstSimpleName, o);
                    Class<?>[] interfaces = aClass.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        //将接口映射到这个对象
                        ioc.put(anInterface.getName(), o);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private String lowerFirst(String source) {
        /*使用getBytes和toCharArray达到了相同的效果*/
        byte[] bytes = source.getBytes();
        if (bytes[0] >= 'A' && bytes[0] <= 'Z') {
            bytes[0] = (byte) (bytes[0] + 32);
        }
        return new String(bytes);
    }

    private void doScanPackage(String scanPackage) {
        /*需要找到context上下文的路径*/
        String packPath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");
        File file = new File(packPath);
        File[] files = file.listFiles();
        if (files.length == 0) {
            //如果包下面没有类直接进行返回
            return;
        }
        for (File sonFile : files) {
            if (sonFile.isDirectory()) {
                doScanPackage(scanPackage + "." + sonFile.getName());
            } else if (sonFile.isFile()) {
                classNames.add(scanPackage + "." + sonFile.getName().replaceAll(".class", ""));
            }
        }
    }

    private void doLoadConfig(String contextConfigLocation) {
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
        try {
            //加载配置文件
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String requestURI = req.getRequestURI();
        Handler handler = getHandler(requestURI);
        if (handler == null) {
            resp.getWriter().write("404 not found!");
            return;
        }
        Map<String, String[]> parameterMap = req.getParameterMap();
        //组装参数
        Map<String, Integer> parameterIndex = handler.getParameterIndex();
        Object[] args = new Object[parameterIndex.size()];
        for (String paramKey : parameterMap.keySet()) {
            Integer integer = parameterIndex.get(paramKey);
            args[integer] = StringUtils.join(parameterMap.get(paramKey), ",");
        }
        String reqName = HttpServletRequest.class.getSimpleName();
        if (parameterIndex.containsKey(reqName)) {
            args[parameterIndex.get(reqName)] = req;
        }
        String respName = HttpServletResponse.class.getSimpleName();
        if (parameterIndex.containsKey(respName)) {
            args[parameterIndex.get(respName)] = resp;
        }
        try {
            handler.getMethod().invoke(handler.getController(), args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    private Handler getHandler(String requestURI) {
        for (Handler handler : handlerMapping) {
            Matcher matcher = handler.getPattern().matcher(requestURI);
            if (!matcher.matches()) {
                continue;
            } else {
                return handler;
            }
        }
        return null;
    }
}
